Apache DeltaSpike - the CDI toolbox

Antoine Sabot-Durand

  • Senior Software Engineer
  • CDI co-spec lead, Java EE 8 EG
  • Red Hat, Inc.
  • @antoine_sd
  • www.next-presso.com
  • github.com/antoinesd

Rafael Benevides

  • Senior Software Engineer
  • DeltaSpike P.M.C member
  • Red Hat, Inc.
  • @rafabene
  • github.com/rafabene

Agenda

  • CDI Portable Extensions
  • What is DeltaSpike ?
  • Core Module
  • Other DeltaSpike Modules
  • Question & Answers

CDI Portable Extensions

Portable extensions

OCP (Open Closed Principle) in CDI
OCP final

Extensions, what for?

To integrate 3rd party libraries, frameworks or legacy components
To change existing configuration or behavior
To extend CDI and Java EE
Thanks to them, Java EE can evolve between major releases

Extensions, how?

rubik
Implement javax.enterprise.inject.spi.Extension
Register the Extension
Observe SPI events at boot time related to the bean manager lifecycle

More concretely

Service provider of the service javax.enterprise.inject.spi.Extension declared in META-INF/services
Just put the fully qualified name of your extension class in this file
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.Extension;

public class CdiExtension implements Extension {

    void beforeBeanDiscovery(@Observes BeforeBeanDiscovery bbd) {
    }
    //...

    void afterDeploymentValidation(@Observes AfterDeploymentValidation adv) {
    }
}

Example: Injecting a String from a Properties file

    @Inject @Property("key1")
    private String property1;

    @Inject @Property("key2")
    private String property2;
It can be achieved by @Produces but it could lead to: Unsatisfied dependencies for type String with qualifiers @Property…​
    @Produces
    @Property("key1")
    public String propriedade1Producer()
    {
        return propertiesFile.getProperty("key1");
    }

Solution: Create a CDI Portable Extension

powerful
One of the most powerful feature of the CDI specification
Not really popularized, partly due to:
  1. Their high level of abstraction
  2. The good knowledge on Basic CDI and SPI
  3. Lack of information (CDI is often reduced to a basic DI solution)

What is DeltaSpike ?

CDI & DeltaSpike

hook
CDI is a specification. It doesn’t provide business features
but it includes a powerful hook to add these business features
The "Poortable extensions" feature is this hook
Thanks to it, CDI can be easily enhanced with new high level features

Apache DeltaSpike is…​

toolbox
A collection of ready to use extensions to help you in your projects
A toolbox to help you develop new CDI portable extensions
A great way to learn how to develop your own extension by browsing the source code
The most obvious entry point to CDI eco-system

Where does it come from ?

TODO Move content to here

A bit of history

TODO Move content to here

Modules and dependencies

TODO Move content to here

Core Module

Core - Exception Handler

public class InventoryActions {
    @PersistenceContext private EntityManager em;
    @Inject private Event<ExceptionToCatchEvent> catchEvent; (1)

    public Integer queryForItem(Item item) {
        try {
          Query q = em.createQuery("SELECT i from Item i where i.id = :id");
          q.setParameter("id", item.getId());
          return q.getSingleResult();
        } catch (PersistenceException e) {
          catchEvent.fire(new ExceptionToCatchEvent(e)); (2)
        }
    }
}
1The Event of generic type ExceptionToCatchEvent is injected into your class for use later within a try/catch block.
2The event is fired with a new instance of ExceptionToCatchEvent constructed with the exception to be handled.

Core - Exception Handler

Exceptions are handled asynchronously.
@ExceptionHandler  (1)
public class MyHandlers {
    void printExceptions(@Handles ExceptionEvent<Throwable> evt) { (2)
        System.out.println("Something bad happened:" +
        evt.getException().getMessage());
        evt.handleAndContinue(); (3)
    }
}
1Exception handler methods are registered on beans annotated with @ExceptionHandler
2The @Handles annotation on the first parameter designates this method as an exception handler.
3This handler does not modify the invocation of subsequent handlers, as designated by invoking handleAndContinue().

Core - Type-safe ProjectStage

The current ProjectStage can be injected.
@Inject
private ProjectStage projectStage;

//...

boolean isDevProjectStage = ProjectStage.Development.equals(this.projectStage);
You can also use the ProjectStage at XHTML files.
<h:panelGroup layout="block"rendered="#{applicationConfig.projectStage == 'Development'}" >
    <!-- HTML Snippet is shown only in Development stage -->
</h:panelGroup>

Core - Type-safe ProjectStage

Besides custom ProjectStages it is possible to use the following pre-defined ProjectStages:
  1. UnitTest
  2. Development
  3. SystemTest
  4. IntegrationTest
  5. Staging
  6. Production
It can be set using DeltaSpike Configuration
-D org.apache.deltaspike.ProjectStage=Development

Core - @Exclude

It’s like @Vetoed from CDI 1.1 but better!
Excluding a Bean in any Case
@Exclude
public class NoBean{  }
Excluding a Bean in Case of ProjectStageDevelopment
@Exclude(ifProjectStage = ProjectStage.Development.class)
public class MyBean{  }
Excluding a Bean if the ProjectStage is different from Development
@Exclude(exceptIfProjectStage = ProjectStage.Development.class)
public class MyDevBean{ }
Excluding a Bean based on an Expression which Evaluates to True
@Exclude(onExpression = "db==prodDB")
public class DevDbBean {  }

Core - DeltaSpike Configuration Mechanism

Configuration API
String userName = ConfigResolver.getPropertyValue("user.name");  (1)
String dbUserName = ConfigResolver.getPropertyAwarePropertyValue("db.username"); (2)
Integer dbPort = ConfigResolver
    .resolve("db.port")  (3)
    .as(Integer.class)
    .withProjectStage(true)
    .withDefault(3306)
    .getValue();
Date deadline = ConfigResolver.resolve("project.deadline") (4)
  .as(Date.class, new CustomDateConverter()).getValue());
Properties
user.name = "Rafael"  (1)
db.username.Production = "Antoine" (2)
db.username.Development = "Benevides" (2)
db.port = 1234 (3)
project.deadline = 2017-04-01 (4)

Core - DeltaSpike Configuration Mechanism

Injection of configured values into beans using @ConfigProperty
@ApplicationScoped
public class SomeRandomService
{
    @Inject
    @ConfigProperty(name = "endpoint.poll.interval")
    private Integer pollInterval;

    @Inject
    @ConfigProperty(name = "endpoint.poll.servername")
    private String pollUrl;

    ...
 }

Core - DeltaSpike Configuration Mechanism

How to provide these Properties to DeltaSpike?

By default there are implementations for the following configuration sources (listed in the lookup order):
  1. System properties
  2. Environment properties
  3. JNDI values - the base name is "java:comp/env/deltaspike/"
  4. Properties file values - default filename is "META-INF/apache-deltaspike.properties"
You can also specify your own config file or create a custom ConfigSource (example: read from XML, JSON, DB, etc)

Core - Injecting Resources

DeltaSpike has simple APIs for performing basic resource loading and property file reading.
@Inject
@InjectableResource("myfile.properties")
private InputStream is;

public String getVersion() throws IOException {
    try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
      return br.readLine();
    }
}
The InjectableResourceProvider interface can be implemented to allow reading from alternate sources if needed (e.g. database LOBs, NoSQL storage areas).

Core - Messages and i18n

Type-safe messages - Bean creation
@Named("msg")
@MessageBundle
public interface MyMessages {

    public String welcome();

    //in the message bundle: welcometo=Welcome to %s
    public String welcomeTo(String username);

    //in the message bundle: custom_message=DeltaSpike is awesome!
    @MessageTemplate("{custom_message}")
    public String message();
}

--> Create the Bundle files in the same package <--
org/apache/deltaspike/example/message/MyMessages.properties
org/apache/deltaspike/example/message/MyMessages_en.properties
org/apache/deltaspike/example/message/MyMessages_de.properties

Core - Messages and i18n

Now the messages bean is ready to be used in Java Classes
@Inject
private MyMessages messages;
//
new FacesMessage(messages.welcomeTo("Rafael"));
log.info(messages.message());
…​or even inside JSF because it uses a @Named annotation.
<h1>#{msg.welcome}</h1>

Security Module

Security Module - Simple interceptor-style authorization

Create the Annotation and the authorizer
@Retention(value = RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@SecurityBindingType
public @interface AdminOnly {
}

@ApplicationScoped
public class ApplicationAuthorizer
{
    @Secures
    @AdminOnly
    public boolean verifyPermission(InvocationContext invocationContext, BeanManager manager, @Loggged User user) throws Exception {
      return user.getRole().equalsIgnoreCase("Admin");
    }
}

Security Module - Simple interceptor-style authorization

Now this annotation can be used in any method
@ApplicationScoped
public class SecuredBean {

    @AdminOnly
    public void doSomething() {
        //...
    }
}

Data Module

Data Module

Data module is an implementation of the repository pattern.
At the moment it only support RDBMS thru JPA.
But it could be extended to support other data services.

Repository pattern

"A Repository represents
all objects of a certain type
as a conceptual set.

It acts like a collection,
except with more elaborate
querying capability."

-Eric Evans (in Domain Driven Design)

Data Module - Repository definition

@Repository
public interface UserRepository extends EntityRepository<User, Long> {

/* DeltaSpike creates a proxy which implements:
count();
findAll();
findBy(PK);
flush();
refresh();
remove();
save();
saveAndFlush();
*/

}
It uses the “partial bean” module to dynamically create implementation at runtime.

Data Module - Making queries

@Repository
public interface UserRepository extends EntityRepository<User, Long> {

  public User findByUsernameAndPassword(String username, char[] password); (1)

  @Query("SELECT u FROM User AS u WHERE u.role in (?1)") (2)
  public List<Role> findByRoles(List<Role> roles);

}
1The name of the method automatically creates the query. Example:
"SELECT u FROM User u WHERE u.username = ?1 AND u.password = ?2 "
2The query is defined inside the @Query annotation.

JSF Module

JSF Module - JSF Messages

@MessageBundle
public interface Messages {

    @MessageTemplate("Welcome to DeltaSpike")
    String welcomeToDeltaSpike();

}

@Model
public class MyJSFBean {

    @Inject
    private JsfMessage<Messages> messages;

    //...
    messages.addInfo().welcomeToDeltaSpike();
}

JSF Module - @WindowScoped

"The window-scope is like a session per window"
@WindowScoped
public class PreferencesBean implements Serializable {
    //...
}
"There isn’t a lot of use-cases which need shared data between windows"

JSF Module - Double-Submit Prevention

"To avoid that the same content of a form gets submitted and therefore processed multiple times"
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ds="http://deltaspike.apache.org/jsf">
    <h:head>
        <!-- head content -->
    </h:head>
    <h:body>
        <h:form>
            <!-- form content -->
            <ds:preventDoubleSubmit/>
        </h:form>
    </h:body>
</html>

Scheduler Module

Scheduler Module

Provides integration with Quartz.
// Job will execute each minute
@Scheduled(cronExpression = "0 0/1 * * * ?", onStartup = false)
public class CdiAwareQuartzJob implements org.quartz.Job {

    // And it can receive CDI injections
    @Inject
    private AdminServices service;

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        service.executeAdministrativeTask();
    }
}
@Inject
private Scheduler<Job> jobScheduler;

//...
jobScheduler.registerNewJob(CdiAwareQuartzJob.class);